#include "logic.h"
#include <ctime>
#include <cstdlib>

Logic::Logic( int _level ) :
    m_solution( 0 ),
    m_elem( 0 )
{
    // Defaultinitialisierung von Loesungs - und Raetselmatrizen
    for( int row = 0; row < SIZE; row++ )
        for( int column = 0; column < SIZE; column++ )
        {
            m_grid9x9_ready[ row ][ column ] = 0;
            m_grid9x9_user[ row ][ column ] = 0;
        }

    while( true )
    {
        if( create_game( _level ) )
            return;
    }
}


Logic::~Logic(){}

int Logic::getNumberReady( int _row, int _column ) const
{
    return m_grid9x9_ready[_row][_column];
}

int Logic::getNumberUser( int _row, int _column ) const
{
    return m_grid9x9_user[_row][_column];
}

void Logic::setNumberUser( int _row, int _column, int _value )
{
    m_grid9x9_user[_row][_column] = _value;
}


bool Logic::create_game( int _level )
{
    srand( time( NULL ) ); // rand()-Initialisierung

    // if-Schleife garantiert, dass das richtige Logicku immer erstellt wird
    if( !create_sudo9x9( 0, 0, *m_grid9x9_ready ) )
        return false;

    copyGrid9x9(); // Sudoku in 2-e Matrix kopiert
    removeRandomCells( _level ); // zufaellige Zahlen werden fuer Spieler ausgeblendent(entfernt)

    return true;
}


bool Logic::create_sudo9x9( int _row, int _column, int* _grid )
{
    int rand1 = 1 + rand()%9; // zufaellige Zahl generiert

     // Zeile geprueft und, wenn's noetig ist, ausgetauscht
    if( _column > 8 )
    {
        _row++;
        _column = 0;

        // Spalte geprueft
        if( _row > 8 )
             return true;
    }

    if( !check( _row, _column, rand1, _grid ) ) // Spielregeln geprueft
    {
        _grid [ _row * SIZE + _column ] = rand1;

        if( create_sudo9x9( _row, _column + 1, _grid ) ) //naechste Zelle geprueft
             return true;
    }

    else   // wenn nicht, dann naechste Zahl testen
    {
        for( int i = 1; i <= 9; i++ )
            if( !check( _row, _column, i, _grid ) )
            {
                _grid [ _row * SIZE + _column ] = i;
                if( create_sudo9x9( _row, _column + 1, _grid ) ) //naechste Zelle geprueft
                     return true;
            }
    }

    _grid[ _row * SIZE + _column ] = 0; // wenn keine Zahl passt, rueckgaengig
        return false;  // Misserfolg
}


int Logic::countEmpty()
{
    int emptyCells = 0; // Anfangsinitialisierung

    for( int row = 0; row < SIZE; row++ )
        for( int column = 0; column < SIZE; column++ )
             if( m_grid9x9_user[ row ][ column ] == 0 )
                 emptyCells++;

    return emptyCells; // Rueckgabe
}

void Logic::removeRandomCells( int _level )
{
    int removable; // zu entfernende Zahlen

    // Temporaere Struktur
    struct Zelle{
        int row;
        int column;
    };

    Zelle * zell = new Zelle[ SIZE * SIZE ]; // Hilfsfeld


    if( _level  == 1 ) // Easy
                    removable = 40;
    if( _level  == 2 ) // Medium
                    removable = 55;
    if( _level  == 3 ) // Hard
                    removable = 70;

    for( int row = 0; row < SIZE; ++row ) // Feld wird mit Zahlen gefuellt
        for( int column = 0; column < SIZE; ++column )
        {
            zell[ m_elem ]. row = row;
            zell[ m_elem ].column = column;
            ++m_elem;
        }

    while( countEmpty() < removable ) // Laeuft bis noetige Anzahl entfernt ist
    {
        if( m_elem <= 0 )
            break;

        int rand2 = rand()%m_elem;

        int tmp1 = m_grid9x9_user[ zell[ rand2 ].row ][ zell[ rand2 ].column ];
        int tmp2 = m_grid9x9_user[ ( SIZE - 1 ) - zell[ rand2 ].row ][ ( SIZE - 1 ) - zell[ rand2 ].column ];

        m_grid9x9_user[ zell[ rand2 ].row ][ zell[ rand2 ].column ] = 0;
        m_grid9x9_user[ ( SIZE - 1 ) - zell[ rand2 ].row ][ ( SIZE - 1 ) - zell[ rand2 ].column ] = 0;

        //wenn nicht eindeutig loesbar, dann rueckgaengig machen
        if( !checkUnique( *m_grid9x9_user ) )
        {
            m_grid9x9_user[ zell[ rand2 ].row ][ zell[ rand2 ].column ] = tmp1;
            m_grid9x9_user[ ( SIZE - 1 ) - zell[ rand2 ].row ][ ( SIZE - 1 ) - zell[ rand2 ].column ] = tmp2;
        }

        // gepruefte Zellen aus Feld entfernen
        for ( int i = rand2; i < m_elem; ++i )
             zell[ i ] = zell[ i + 1 ];
        m_elem--;

        if( zell[ m_elem - rand2 ].row != 4 && zell[ m_elem - rand2 ].column != 4 )
        {
            for( int i = ( m_elem - rand2 ); i < m_elem; ++i)
                zell[ i ] = zell[ i + 1 ];
            m_elem--;
        }
    }

        delete [] zell;
}

void Logic::copyGrid9x9()
{
    for( int row = 0; row < SIZE; row++ )
            for( int column = 0; column < SIZE; column++ )
                    m_grid9x9_user[ row ][ column ] = m_grid9x9_ready[ row ][ column ];
}

bool Logic::checkUnique( int* _grid )
{
    m_solution = 0;
    countSolutions( 0, 0, _grid );

    if( m_solution == 1 ) // eindeutig
            return true;
    else              // mehrdeutig
            return false;
}

int Logic::countSolutions( int _row, int _column, int* _grid )
{
    // Zeile geprueft
    if( _column == SIZE )
    {
        _row++;
        _column = 0;

        // Spalte geprueft
        if( _row == SIZE )
            return 1;
    }

    if( _grid[ _row * SIZE + _column ] > 0 )
        return countSolutions( _row, _column + 1, _grid );

    for( int i = 1; i <= 9; i++ ) // Loesungen werden gezaehlt
        if( !check( _row, _column, i, _grid ) )
        {
            _grid[ _row * SIZE + _column ] = i;
            if( countSolutions( _row, _column + 1, _grid ) )
                m_solution++;
        }

    _grid[ _row * SIZE + _column ] = 0;
        return 0;
}


bool Logic::check( int _row, int _column,  int _number, int* _grid )
{
    if( checkRow( _row, _number, _grid ) )
        return true;
    if( checkColumn( _column, _number, _grid ) )
        return true;
    if( checkBox( _row, _column, _number, _grid ) )
        return true;

    return false;
}


bool Logic::checkRow( int _row, int _number, int* _grid )
{
    for(int i = 0; i < SIZE; ++i)
        if( _grid[ _row * SIZE + i ] == _number )
           return true;

    return false;
}


bool Logic::checkColumn( int _column, int _number, int* _grid )
{
    for( int i = 0; i < SIZE; i++ )
        if( _grid[ i * SIZE + _column ] == _number )
          return true;

    return false;
}


bool Logic::checkBox( int _row, int _column, int _number, int* _grid )
{
    int row_box = ( _row / 3 ) * 3; // erste Zeile von Subgrid bestimmen
    int column_box = ( _column / 3 ) * 3; // erste Spalte von Subgrid bestimmen

    for( int row = row_box; row < row_box + 3; row++ )
        for( int column = column_box; column < column_box + 3; column++ )
            if( _grid[ row * SIZE + column ] == _number )
                return true;

      return false;
}
bool Logic::isFinished()
{
    for( int row = 0; row < SIZE; row++ )
        for( int column = 0; column < SIZE; column++ )
            if( m_grid9x9_user[ row ][ column ] == 0 )
                return false;

    return true;
}
